package cn.zyjblogs.netty.codec;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.util.NettyRuntime;

public class NettyServer {
    public static void main(String[] args) {
        System.out.println("cpu核数: " + NettyRuntime.availableProcessors());

        /**
         *  创建BossGroup 和WorkerGroup
         * 说明
         * 1、创建两个线程组 bossGroup和WorkerGroup
         * 2、bossGroup 只是处理连接请求，真正的和客户端业务处理，会交给workerGroup完成
         * 3、两个都是无限循环
         * 4、bossGroup 和workerGroup 含有的子线程的个数
         *     默认实际cpu核数 * 2
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //创建服务器端的启动对象，配置参数
        ServerBootstrap bootstrap = new ServerBootstrap();
        //使用链式编程来进行设置 设置两个线程组
        bootstrap.group(bossGroup, workerGroup)
                //使用NioServerSocketChannel 作为服务器的通道实现
                .channel(NioServerSocketChannel.class)
                //设置线程队列得到的连接数
                .option(ChannelOption.SO_BACKLOG, 128)
                //设置保持活动的连接状态
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //给workerGroup的EventLoop对应的管道设置处理器
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    //给pipeline设置处理器
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        System.out.println("客户端socketchannel hashcode= " + ch.hashCode());
                        ch.pipeline()
                                //需要指定对那种对象进行解码
                                .addLast("decoder",new ProtobufDecoder(StudentPO.Student.getDefaultInstance()))
                                .addLast(new NIOServerHandler());
                    }
                });
        System.out.println(".....服务器 is ready.....");
        try {
            //绑定一个端口并且同步，生成了一个ChannelFuture对象
            ChannelFuture cf = bootstrap.bind(6668).sync();
            cf.addListener((ChannelFuture channelFuture) -> {
                if (channelFuture.isSuccess()) {
                    System.out.println("监听端口 6668 成功");
                } else {
                    System.err.println("监听端口 6668 失败");
                }
            });
            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } catch (Exception e) {
            System.out.println("启动报错了" + e.getMessage());
        }finally {
            //优雅关闭
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
